<?php

namespace Tictactoe\Ai;

use Tictactoe\Game as Game;
use Tictactoe\Tool as Tool;
use Tictactoe\Ai\Population\Item as Item;
use Tictactoe\Ai\Estimate as Estimate;

class Move {

    /**
     * @var Game
     */
    protected $game = null;

    /**
     * @var int 
     */
    protected $iterationQty = 2;

    /**
     * @param Game $game
     */
    public function __construct(Game $game) {
        $this->setGame($game);
    }

    /**
     * @return Game
     */
    public function getGame() {
        return $this->game;
    }

    /**
     * @param Game $game
     */
    public function setGame(Game $game) {
        $this->game = $game;
    }

    /**
     * @return int
     */
    public function getIterationQty() {
        return $this->iterationQty;
    }

    /**
     * @param int $iterationQty
     */
    public function setIterationQty($iterationQty) {
        $this->iterationQty = $iterationQty;
    }

    /**
     * Wykonuje ruch w grze dla konkretnej wartosci 1 - kółko (O) lub 2 - krzyżyk (X)
     * 
     * @param int $v
     * @return Item
     */
    public function getNextMoveForValue($v) {


        $oponentValue = Tool::getOponentValue($v);

        $siblingValue = $v;
        if ($this->getGame()->isFirstMoveForValue($v)) {
            $siblingValue = $oponentValue;
        }

        /**
         * generowanie populacji AI z ktorej zostanie wybrany najlepszy mozliwy ruch
         */
        $population = new Population();
        $population->setPopulationQty(11 * 11);
        $population = $this->getPopulationForValue($population, $this->getGame(), $v, $siblingValue);

        /**
         * czy najleszy ruch AI to ruch wygrywajacy
         */
        $items = $population->getItems();
        $best = $this->getBestItem($items);
        if ($best->getEstimate() >= Estimate::WINNER) {
            $best->setInfo('AI population, Estimate::WINNER ');
            return $best;
        }

        
        
        
        /**
         * weryfikacja osobnikow tej populacji
         */
        $populationTemp = new Population();
        $populationTemp->setPopulationQty($population->getPopulationQty());

        foreach ($population->getItems() as $key => $item) {
            /**
             * 1. wygenerowanie ruchow przeciwnika na grze z osobnika
             * 2. jezeli osobnik w ktoryms ruchu wygrywa to usuwamy osobnika z populacji pierwotnej
             */
            $populationTemp = $this->getPopulationForValue($populationTemp, $item->getGame(), $oponentValue, $oponentValue);
        }
        
        $items = $populationTemp->getItems();
        $bestO = $this->getBestItem($items);
        $bestO->setForValue($v);

        /**
         * czy najlepszy ruch przeciwnika jest wygrywajacy
         */
        if ($bestO->getEstimate() >= Estimate::WINNER) {
            $bestO->setInfo('Oponent population, Estimate::WINNER ');
            return $bestO;
        }

        /**
         * czy najlepszy ruch przeciwnika niebezpieczny
         */
        if ($bestO->getEstimate() >= Estimate::WINNER_NEXT) {
            
            if ($best->getEstimate() >= Estimate::WINNER_NEXT) {
                $best->setInfo('Oponent population, Estimate::WINNER_NEXT but MY move is first !');
                return $best;
            }
            
            $bestO->setInfo('Oponent population, Estimate::WINNER_NEXT ');
            return $bestO;
        }





        /**
         * brak sytuacji wygrywjacyh i niebezpiecznych
         * wykonujemy najlepszy ruch AI
         */
        $best->setInfo('algorythm ended');
        return $best;
    }

    /**
     * @param Game $game
     * @param int $v
     * @param int $qty
     * @param int $siblingValue
     * @return Population
     */
    protected function getPopulationForValue(Population $population, Game $game, $v, $siblingValue = 0) {

        $size = $game->getSize();
        for ($x = 0; $x < $size; $x++) {
            for ($y = 0; $y < $size; $y++) {

                $value = $game->getValue($x, $y);

                if ($value == 0 && ($siblingValue === 0 || $game->hasSiblings($x, $y, $siblingValue))) {
                    $gameClone = clone $game;
                    $gameClone->setValue($x, $y, $v);
                    $population->addItem($gameClone, $x, $y, $v);
                }
            }
        }

        return $population;
    }

    /**
     * @param Item[] $items
     * @return array
     * @throws Exception
     */
    public function getBestItem($items) {
        /**
         * wybranie najlepszych osobnikow
         */
        $res = array();
        $estimateMax = 0;
        foreach ($items as $one) {
            if ($one->getEstimate() == $estimateMax) {
                // ta sama estymacja
                $res[] = $one;
            } if ($one->getEstimate() > $estimateMax) {
                // estymacja wieksza
                $res = array();
                $res[] = $one;
                $estimateMax = $one->getEstimate();
            } else {
                // estymacja mniejsza
                continue;
            }
        }

        if (count($res) <= 0) {
            throw new Exception('There is not population item in population set !');
        }


        shuffle($res);

        return $res[0];
    }
}
